//! # Query
//!
//! query is the core module of this crate

use std::{collections::HashMap, usize};

use crate::{
    parser::{Element, Junction, Node, Op, Parser},
    Error,
};

/// define the type of result
pub type ParseResult = Result<Parser, Error>;

macro_rules! vec_ops {
    (@add $self:expr, $rhs:expr) => {{
        let mut res = vec![];
        for item in $self {
            if $rhs.contains(&item) {
                res.push(item);
            }
        }
        res.sort();
        res
    }};

    (@or $self:expr, $rhs:expr) => {{
        let mut res = $self;
        for item in $rhs {
            if !res.contains(&item) {
                res.push(item);
            }
        }

        res.sort();

        res
    }};

    (@diff $self:expr, $rhs:expr) => {{
        let mut res = vec![];
        for item in $rhs {
            if !$self.contains(&item) {
                res.push(item);
            }
        }

        res.sort();

        res
    }};
}

#[derive(Debug, PartialEq)]
pub enum Number {
    Int(i64),
    Float(f32),
}

impl Number {
    /// force into float number
    pub fn into_float(&self) -> f32 {
        match self {
            Self::Float(f) => *f,
            Self::Int(n) => *n as f32,
        }
    }

    /// print string
    pub fn print(&self) -> String {
        match self {
            Self::Float(f) => format!("{}", f),
            Self::Int(n) => format!("{}", n),
        }
    }
}

impl From<isize> for Number {
    fn from(n: isize) -> Self {
        Self::Int(n as i64)
    }
}

impl From<i64> for Number {
    fn from(n: i64) -> Self {
        Self::Int(n)
    }
}

impl From<usize> for Number {
    fn from(n: usize) -> Self {
        Self::Int(n as i64)
    }
}

impl From<f32> for Number {
    fn from(f: f32) -> Self {
        Self::Float(f)
    }
}
#[derive(Debug, PartialEq)]
pub enum Value {
    String(String),
    Number(Number),
}

impl From<&str> for Value {
    fn from(s: &str) -> Self {
        Self::from(s.to_string())
    }
}

impl From<String> for Value {
    fn from(s: String) -> Self {
        if let Ok(n) = s.parse::<i64>() {
            return Self::Number(Number::Int(n));
        } else if let Ok(n) = s.parse::<f32>() {
            return Self::Number(Number::Float(n));
        } else {
            Self::String(s)
        }
    }
}

impl From<i64> for Value {
    fn from(n: i64) -> Self {
        Self::Number(Number::from(n as isize))
    }
}

impl From<i32> for Value {
    fn from(n: i32) -> Self {
        Self::Number(Number::from(n as isize))
    }
}

impl From<isize> for Value {
    fn from(n: isize) -> Self {
        Self::Number(Number::from(n))
    }
}

impl From<usize> for Value {
    fn from(n: usize) -> Self {
        Self::Number(Number::from(n))
    }
}

impl From<f32> for Value {
    fn from(f: f32) -> Self {
        Self::Number(Number::from(f))
    }
}

/// trait for Query
///
/// it can be used in outer crate
pub trait List {
    type Output;
    fn order_list(&self) -> Vec<usize>;
    fn filter_list(&self, list: &[usize]) -> Vec<&Self::Output>;
}

/// allow to do query
pub trait QueryAble: List {
    fn query<P>(&self, parser: P) -> Result<Vec<&Self::Output>, Error>
    where
        P: AsRef<Parser>,
    {
        Ok(self.filter_list(&self.parse_query(&parser)?))
    }

    fn query_check<P>(&self, parser: &Result<P, Error>) -> Result<Vec<&Self::Output>, Error>
    where
        P: AsRef<Parser>,
    {
        self.query(parser.as_ref().map_err(|e| e.clone())?)
    }

    fn parse_element(&self, element: &Element) -> Vec<usize>;

    fn parse_query<P>(&self, parser: P) -> Result<Vec<usize>, Error>
    where
        P: AsRef<Parser>;
}

/// implement of Vec<T>
impl<T> List for Vec<T> {
    type Output = T;
    /// query filter data due to a including-list
    /// # Examples
    ///```
    /// use magic_query::List;
    ///
    /// let filter1 = vec![0, 2];
    /// let list1 = vec![1, 10, 100, 1000];
    /// assert_eq!(vec![&1, &100], list1.filter_list(&filter1));
    ///
    /// let filter2 = vec![1, 3, 4];
    /// let list2 = vec![1, 2, 10, 20, 3, 30];
    /// assert_eq!(vec![&2, &20, &3], list2.filter_list(&filter2));
    ///```
    fn filter_list(&self, list: &[usize]) -> Vec<&Self::Output> {
        self.iter()
            .enumerate()
            .filter(|(i, _)| list.contains(i))
            .map(|(_, v)| v)
            .collect()
    }

    /// print the order list of a vec
    /// # Example
    ///```
    /// use magic_query::List;
    ///
    /// let a = vec![0, 2, 4, 5, 6];
    /// let b = vec![0, 1, 2, 3, 4];
    /// assert_eq!(a.order_list(), b);
    ///```
    fn order_list(&self) -> Vec<usize> {
        self.iter().enumerate().map(|(i, _)| i).collect()
    }
}

/// basic data map
///
/// store the data inner itself
#[derive(Debug)]
pub struct Map {
    pub(crate) _inner: Vec<MapItem>,
}

/// the item of Map
pub type MapItem = HashMap<String, Value>;

impl<T, U> From<Vec<HashMap<T, U>>> for Map
where
    T: ToString,
    U: Into<Value>,
{
    fn from(v: Vec<HashMap<T, U>>) -> Self {
        let _inner = v
            .into_iter()
            .map(|e| {
                let mut map = HashMap::new();
                for (k, v) in e {
                    map.insert(k.to_string(), v.into());
                }

                map
            })
            .collect();
        Self { _inner }
    }
}

impl std::ops::Deref for Map {
    type Target = Vec<MapItem>;

    fn deref(&self) -> &Self::Target {
        &self._inner
    }
}

impl List for Map {
    type Output = MapItem;

    fn filter_list(&self, list: &[usize]) -> Vec<&Self::Output> {
        self._inner.filter_list(list)
    }

    fn order_list(&self) -> Vec<usize> {
        self._inner.order_list()
    }
}
macro_rules! gen_query {

    (@err) => {
        Error::Query(format!("parsing list is empty"))
    };

    (@eq $res:expr, $i:expr => $k:expr, $v:expr, $val:expr) => {
        if $k.eq($v) == $val {
            $res.push($i);
        }
    };


    (@math $res:expr, $i:expr => $k:expr, $v:expr, $($op:tt)*) => {
        match ($k, $v) {
            (Value::Number(k), Value::Number(v)) => {
                if let (Number::Int(k), Number::Int(v)) = (k, v) {
                    if k $($op)* v {
                        $res.push($i);
                    }
                } else if k.into_float() $($op)*  v.into_float() {
                    $res.push($i);
                }
            }
            _ => {}
        }
    };

    (@cons $res:expr, $i:expr => $k:expr, $v:expr, $val:expr) => {
        match ($k, $v) {
            (Value::String(k), Value::String(v)) => {
                if k.contains(v) == $val {
                    $res.push($i);
                }
            }

            (Value::String(k), Value::Number(v)) => {
                if k.contains(&v.print()) == $val {
                    $res.push($i);
                }
            }

            (Value::Number(k), Value::Number(v)) => {
                if k.print().contains(&v.print()) == $val {
                    $res.push($i);
                }
            }
            _ => {}
        }
    }
}

impl QueryAble for Map {
    fn parse_query<P>(&self, parser: P) -> Result<Vec<usize>, Error>
    where
        P: AsRef<Parser>,
    {
        let mut list = vec![];
        let mut ops_list: Vec<&Junction> = vec![];
        for node in parser.as_ref().iter() {
            match node {
                Node::Element(e) => {
                    let value = self.parse_element(e);
                    if let Some(junc) = ops_list.pop() {
                        let pre = list.pop().ok_or(gen_query!(@err))?;
                        match junc {
                            Junction::Or => list.push(vec_ops!(@or value,pre)),
                            Junction::And => list.push(vec_ops!(@add value,pre)),
                            Junction::Diff => list.push(vec_ops!(@diff value,pre)),
                            Junction::Null => {}
                        }
                    } else {
                        list.push(value);
                    }
                }
                Node::Junction(junc) => {
                    ops_list.push(junc);
                }
            }
        }

        list.get(0).ok_or(gen_query!(@err)).map(|s| s.to_owned())
    }

    fn parse_element(&self, elem: &Element) -> Vec<usize> {
        let mut res = vec![];
        let Element(k, op, v) = elem;

        for (i, map) in self.iter().enumerate() {
            if map.get(k).is_none() {
                res.push(i);
                continue;
            }
            let k = map.get(k).unwrap();
            match op {
                Op::Eql => gen_query!(@eq res, i => k, v, true),
                Op::Dif => gen_query!(@eq res, i => k, v, false),
                Op::Gre => gen_query!(@math res, i => k, v, >),
                Op::Les => gen_query!(@math res, i => k, v, <),
                Op::Ege => gen_query!(@math res, i => k, v, >=),
                Op::Ele => gen_query!(@math res, i => k, v, <=),
                Op::Con => gen_query!(@cons res, i => k, v, true),
                Op::Nco => gen_query!(@cons res, i => k, v, false),
            }
        }

        res
    }
}
